﻿using System;
using System.Reflection;
using System.Windows.Media;
using YourLastOptionsDialog.Core.Attributes;
using YourLastOptionsDialog.ViewModels.Options;

namespace YourLastOptionsDialog
{
    /// <summary>
    /// Extends the options factory to provide support for additional property types,
    /// in particular for <see cref="TimeSpan"/> and <see cref="Color"/> properties.
    /// </summary>
    public class ExtrasOptionsFactoryExtender : IOptionsFactoryExtender
    {
        #region Implementation of IOptionsFactoryExtender

        /// <summary>
        /// Determines the types that should be used for a given property.
        /// The extender should set types only if it is capable of handling the type of property.
        /// If the property should be handled by the existing <see cref="OptionsFactory"/>, the extender
        /// is supposed to simply leave the passed in type references as-is.
        /// </summary>
        /// <param name="property">The property the types need to be determined for.</param>
        /// <param name="optionType">Type of the option to use for the given property.</param>
        /// <param name="optionAttributeType">Type of the option attribute to use for the given property.</param>
        public virtual void DetermineTypesFor(PropertyInfo property, ref Type optionType, ref Type optionAttributeType)
        {
            if (property == null)
            {
                throw new ArgumentNullException("property");
            }

            if (property.PropertyType == typeof(TimeSpan))
            {
                optionType = typeof(TimeSpanOption);
                optionAttributeType = typeof(OptionTimeSpanAttribute);
            }
            else if (property.PropertyType == typeof(Color))
            {
                optionType = typeof(ColorOption);
                optionAttributeType = typeof(OptionColorAttribute);
            }
        }

        /// <summary>
        /// Sets the custom values of an option.
        /// </summary>
        /// <param name="property">The property the option was created for.</param>
        /// <param name="option">The option that needs its custom values to be set.</param>
        /// <param name="optionAttribute">The option attribute that was applied to the property, if available.</param>
        /// <param name="instance">The instance the property value can be found on.</param>
        public virtual void SetCustomValues(PropertyInfo property, Option option, OptionAttribute optionAttribute, object instance)
        {
            var timeSpanOption = option as TimeSpanOption;
            if (timeSpanOption != null)
            {
                SetCustomTimeSpanValue(timeSpanOption, optionAttribute as OptionTimeSpanAttribute);
            }

            // no need to set custom values for the color option, because it has none
        }

        private static void SetCustomTimeSpanValue(TimeSpanOption option, OptionTimeSpanAttribute attribute)
        {
            if (attribute != null)
            {
                // set the maximum and step
                option.Maximum = ConvertFromString(attribute.Maximum, TimeSpan.MaxValue);
                option.Step = ConvertFromString(attribute.Step, TimeSpan.FromSeconds(1.0));
            }
        }

        private static TimeSpan ConvertFromString(string timeSpanString, TimeSpan defaultValue)
        {
            if (string.IsNullOrEmpty(timeSpanString))
            {
                return defaultValue;
            }

            TimeSpan result;
            if (TimeSpan.TryParse(timeSpanString, out result))
            {
                return result;
            }

            return defaultValue;
        }

        #endregion
    }
}
